package net.maergaiyun.service.impl;

import lombok.extern.slf4j.Slf4j;
import net.maergaiyun.component.PayFactory;
import net.maergaiyun.config.RabbitMQConfig;
import net.maergaiyun.constant.TimeConstant;
import net.maergaiyun.controller.request.ConfirmOrderRequest;
import net.maergaiyun.controller.request.ProductOrderPageRequest;
import net.maergaiyun.enums.*;
import net.maergaiyun.exception.BizException;
import net.maergaiyun.interceptor.LoginInterceptor;
import net.maergaiyun.manager.ProductManager;
import net.maergaiyun.manager.ProductOrderManager;
import net.maergaiyun.model.EventMessage;
import net.maergaiyun.model.LoginUser;
import net.maergaiyun.model.ProductDO;
import net.maergaiyun.model.ProductOrderDO;
import net.maergaiyun.service.ProductOrderService;
import net.maergaiyun.util.CommonUtil;
import net.maergaiyun.util.JsonData;
import net.maergaiyun.util.JsonUtil;
import net.maergaiyun.vo.PayInfoVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

    @Autowired
    private ProductOrderManager productOrderManager;

    @Autowired
    private ProductManager productManager;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private PayFactory payFactory;

    @Override
    public Map<String, Object> page(ProductOrderPageRequest request) {
        long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        return productOrderManager.page(request.getPage(), request.getSize(), accountNo, request.getState());
    }

    @Override
    public String queryProductOrderState(String outTradeNo) {
        long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccount(outTradeNo, accountNo);
        if (productOrderDO == null) {
            return "";
        } else {
            return productOrderDO.getState();
        }
    }

    /**
     * 1、防重提交
     * 2、获取最新的流量包价格
     * 3、订单验价
     * 3.1 优惠券和其他折扣
     * 3.2 验证前端显示和后台计算价格
     * 4、创建订单对象保存数据库
     * 5、发送延迟消息-用于自动关单
     * 6、创建支付信息-对接第三方支付
     * 7、回调更新订单状态
     * 8、支付成功创建流量包
     *
     * @param request
     * @return
     */
    @Override
    @Transactional
    public JsonData confirmOrder(ConfirmOrderRequest request) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        String outTradeNo = CommonUtil.getStringNumRandom(32);
        ProductDO productDO = productManager.findDetailById(request.getProductId());
        // 验证价格
        this.checkPrice(productDO, request);
        // 创建订单
        ProductOrderDO productOrderDO = this.saveProductOrder(request, loginUser, outTradeNo, productDO);
        // 创建支付对象
        PayInfoVO payInfoVO = PayInfoVO.builder().accountNo(loginUser.getAccountNo()).outTradeNo(outTradeNo).clientType(request.getClientType()).payType(request.getPayType()).title(productDO.getTitle()).description("").payFee(request.getPayAmount()).orderPayTimeoutMills(TimeConstant.ORDER_PAY_TIMEOUT_MILLS).build();

        // 发送延迟消息
        EventMessage eventMessage = EventMessage.builder().eventMessageType(EventMessageType.PRODUCT_ORDER_NEW.name()).accountNo(loginUser.getAccountNo()).bizId(outTradeNo).build();
        rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(), eventMessage);

        // 调用支付接口
        String payResult = payFactory.pay(payInfoVO);
        if (StringUtils.isNotBlank(payResult)) {
            Map<String, String> resultMap = new HashMap<>(2);
            resultMap.put("mchId", payResult);
            resultMap.put("outTradeNo", outTradeNo);
            return JsonData.buildSuccess(resultMap);
        }

        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
    }

    /**
     * 延迟消息的时间需要比订单过期时间长
     * 查询订单是否存在, 如果已经支付则正常结束
     * 如果订单未支付, 主动调用第三方支付平台查询订单状态
     * (1)确认未支付, 本地取消订单
     * (2)如果第三方平台已经支付, 主动把订单状态改为已支付, 造成该原因的情况可能是支付通道回调有问题
     *
     * @param eventMessage
     * @return
     */
    @Override
    public Boolean closeProductOrder(EventMessage eventMessage) {
        String outTradeNo = eventMessage.getBizId();
        Long accountNo = eventMessage.getAccountNo();

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccount(outTradeNo, accountNo);
        if (productOrderDO == null) {
            log.warn("订单不存在");
            return true;
        }

        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
            log.info("订单已经支付, 直接确认消息: {}", eventMessage);
        }

        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
            PayInfoVO payInfoVO = new PayInfoVO();
            payInfoVO.setPayType(productOrderDO.getPayType());
            payInfoVO.setOutTradeNo(outTradeNo);
            payInfoVO.setAccountNo(accountNo);

            // 从第三方平台查询支付状态
            String payResult = "";
            if (StringUtils.isBlank(payResult)) {
                // 如果为空, 则未支付订单, 本地取消订单
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name());
                log.info("未支付成功, 本地取消订单: {}", eventMessage);
            } else {
                log.warn("支付成功, 但微信回调通知失败, 需要排查问题: {}", eventMessage);
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());

                // 触发成功后的逻辑 TODO
            }
        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData processOrderCallBackMsg(ProductOrderPayTypeEnum productOrderPayTypeEnum, Map<String, String> paramsMap) {
        String outTradeNo = paramsMap.get("out_trade_no");
        String tradeState = paramsMap.get("trade_state");
        Long accountNo = Long.valueOf(paramsMap.get("account_no"));

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccount(outTradeNo, accountNo);

        Map<String, Object> content = new HashMap<>(4);
        content.put("outTradeNo", outTradeNo);
        content.put("accountNo", accountNo);
        content.put("product", productOrderDO.getProductSnapshot());
        content.put("buyNum", productOrderDO.getBuyNum());

        EventMessage eventMessage = EventMessage.builder().bizId(outTradeNo).accountNo(accountNo).messageId(outTradeNo).content(JsonUtil.obj2Json(content)).eventMessageType(EventMessageType.PRODUCT_ORDER_PAY.name()).build();

        if (productOrderPayTypeEnum.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALI_PAY.name())) {
            // 支付宝支付 TODO
        } else if (productOrderPayTypeEnum.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT_PAY.name())) {
            if ("SUCCESS".equalsIgnoreCase(tradeState)) {
                Boolean flag = redisTemplate.opsForValue().setIfAbsent(outTradeNo, "ok", 3, TimeUnit.DAYS);
                if (flag) {
                    log.info("发送支付订单回调消息: {}", eventMessage);
                    rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(), rabbitMQConfig.getOrderUpdateTrafficRoutingKey(), eventMessage);
                }
                return JsonData.buildSuccess();
            }
        }

        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);

    }

    @Override
    public void handleProductOrderMessage(EventMessage eventMessage) {
        String eventMessageType = eventMessage.getEventMessageType();
        if (eventMessageType.equalsIgnoreCase(EventMessageType.PRODUCT_ORDER_NEW.name())) {
            // 关闭订单
            this.closeProductOrder(eventMessage);
        } else if (eventMessageType.equalsIgnoreCase(EventMessageType.PRODUCT_ORDER_PAY.name())) {
            // 订单已经支付, 更新订单状态
            Long accountNo = eventMessage.getAccountNo();
            String outTradeNo = eventMessage.getBizId();
            int rows = productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
            log.info("消费订单更新消息成功, rows={}, eventMessage={}", rows, eventMessage);
        }
    }

    private ProductOrderDO saveProductOrder(ConfirmOrderRequest request, LoginUser loginUser, String outTradeNo, ProductDO productDO) {
        ProductOrderDO productOrderDO = new ProductOrderDO();

        // 设置用户信息
        productOrderDO.setAccountNo(loginUser.getAccountNo());
        productOrderDO.setNickname(loginUser.getUsername());

        // 设置商品信息
        productOrderDO.setProductId(productDO.getId());
        productOrderDO.setProductTitle(productDO.getTitle());
        productOrderDO.setProductSnapshot(JsonUtil.obj2Json(productDO));
        productOrderDO.setProductAmount(productDO.getAmount());

        // 设置订单信息
        productOrderDO.setBuyNum(request.getBuyNum());
        productOrderDO.setOutTradeNo(outTradeNo);
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);

        // 发票信息
        productOrderDO.setBillType(BillTypeEnum.valueOf(request.getBillType()).name());
        productOrderDO.setBillHeader(request.getBillHeader());
        productOrderDO.setBillReceiverEmail(request.getBillReceiverEmail());
        productOrderDO.setBillReceiverPhone(request.getBillReceiverPhone());
        productOrderDO.setBillContent(request.getBillContent());

        // 实际支付总价
        productOrderDO.setPayAmount(request.getPayAmount());
        // 总价(没使用优惠券)
        productOrderDO.setTotalAmount(request.getTotalAmount());
        // 订单状态
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        // 支付类型
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(request.getPayType()).name());

        // 插入数据库
        productOrderManager.add(productOrderDO);
        return productOrderDO;
    }

    private void checkPrice(ProductDO productDO, ConfirmOrderRequest request) {
        BigDecimal bizTotal = BigDecimal.valueOf(request.getBuyNum()).multiply(productDO.getAmount());
        if (bizTotal.compareTo(request.getPayAmount()) != 0) {
            log.error("验证价格失败: {}", request);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }
}
